/**
 * Copyright (C) 2011  Robin Dietrich, Nils Kenneweg, Leonard Stutz
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 **/ 

#ifndef BLOCK_HPP
#define	BLOCK_HPP

#include <OGRE/Ogre.h>
#include <boost/serialization/utility.hpp> 
#include <boost/serialization/collections_save_imp.hpp> 
#include <boost/serialization/collections_load_imp.hpp> 
#include <boost/serialization/split_free.hpp> 
#include <boost/unordered_map.hpp>
#include <boost/lexical_cast.hpp>

namespace minecraft {
    /** typedef for chunk map data type, with increasing block types
     * this only need to changed to `short int` e.g.  */
	typedef unsigned short int b_type;
    /** typedef for state data type */
    typedef unsigned char s_type;
    
    /** highlevel block for plugin system */
    struct block_highlvl {
        short max_hits;
        s_type state_default;
        std::vector<short> sides;
        float max_textures;
        std::string texture_path;
    };
        
    /** block class which should be extended for each new block type */
    class block {
        protected:
            /** id of block type */
            b_type id;
            /** number of hits to take down */
            short max_hits;
            /** default state of block when set / generated */
            s_type state_default;
            /** list of saved state for individual chunks */
            boost::unordered_map<std::string, s_type> state_map;                
            /** number of different textures for this block */
            float max_textures;
	    
        public:
            /** simple array containing the texture positions for each side.
              * order: x, x+1, y, y+1, z, z+1 */
            short sides[6];	
            
            /** constructor */
            block() {}
            
            /** constructor from highlvl block */
            block(block_highlvl b, b_type id) {
                this->id = id;
                for (short i = 0; i < 6; ++i) {
                    this->sides[i] = b.sides[i];
                }
                this->max_textures = b.max_textures;        
        
                Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(this->getTextureName(), "minecraft");
                Ogre::Technique* tech = mat->getTechnique(0);
                Ogre::Pass* pass = tech->getPass(0);
                Ogre::TextureUnitState* tex = pass->createTextureUnitState();
                tex->setTextureName(b.texture_path);
                tex->setNumMipmaps(4);
                tex->setTextureAnisotropy(1);
                tex->setTextureFiltering(Ogre::FO_POINT, Ogre::FO_POINT, Ogre::FO_POINT);
            }
            
            /** destructor */
            virtual ~block() {
                //remove material and texture for this block
                Ogre::MaterialManager::getSingleton().remove(this->getTextureName());
            }
            
            /** returns id of block, inline */
            inline virtual b_type getId() {
                return this->id;
            }
            
            /** returns number of hits before destruction */
            virtual char maxhits() {
                return this->max_hits;
            }
            
            /** returns the name of the texture / material for this block */
            virtual std::string getTextureName() {
                return boost::lexical_cast<std::string>(this->id)+"-mat";
            }
            
            /** returns the path to the texture image */
            virtual std::string getTexturePath() {
                Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(this->getTextureName());
                Ogre::Technique* tech = mat->getTechnique(0);
                Ogre::Pass* pass = tech->getPass(0);
                Ogre::TextureUnitState* tex = pass->getTextureUnitState(0);
                return tex->getTextureName();
            }      
            
            /** returns maximum number of textures used */
            virtual float getMaxTextures() {
                return this->max_textures;
            }
            
            /** called when block is generated / placed */
            virtual void onInit() {};
            /** called when block is destroyed */
            virtual void onRemove() {};
            /** called when block is targeted / hit */
            virtual void onHit() {};
            /** called when block is touched */
            virtual void onTouch() {};
    };
}

namespace boost { 
    namespace serialization {
        /** serializes an unordered map */
        template<class Archive, class Type, class Key, class Hash, class Compare, class Allocator >
        inline void save(Archive & ar, const boost::unordered_map<Key, Type, Hash, Compare, Allocator> &t, const unsigned int) {
            boost::serialization::stl::save_collection<Archive, boost::unordered_map<Key, Type, Hash, Compare, Allocator> >(ar, t);
        }

        /** deserializes an unordered map */
        template<class Archive, class Type, class Key, class Hash, class Compare, class Allocator >
        inline void load(Archive & ar, boost::unordered_map<Key, Type, Hash, Compare, Allocator> &t, const unsigned int) {
            boost::serialization::stl::load_collection<
                Archive, 
                boost::unordered_map<Key, Type, Hash, Compare, Allocator>, 
                boost::serialization::stl::archive_input_map<Archive, boost::unordered_map<Key, Type, Hash, Compare, Allocator> >,
                boost::serialization::stl::no_reserve_imp<boost::unordered_map<Key, Type, Hash, Compare, Allocator>
                > >(ar, t);
        }
        
        /** required for non-intrusive version via << overload when calling serialize on the collection */
        template<class Archive, class Type, class Key, class Hash, class Compare, class Allocator >
        inline void serialize(Archive & ar, boost::unordered_map<Key, Type, Hash, Compare, Allocator> &t, const unsigned int file_version) {            
            boost::serialization::split_free(ar, t, file_version);
        } 
    }
}

#endif	/* BLOCK_HPP */

